package jp.sourceforge.akjrcp.text_douga.audio;

import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;



public class WavUtils {
	public static Log log=LogFactory.getLog(WavUtils.class);
	public static AudioInputStream convertDefaultWavAudioType(File inputFile){
	int nDesiredChannels = 2; //stereo

	/** The sample size in bits the audio data should be converted to.
	*/
	int nDesiredSampleSizeInBits = 16;

	/** The encoding the audio data should be converted to.
	*/
	AudioFormat.Encoding desiredEncoding = AudioFormat.Encoding.PCM_SIGNED;

	/** The sample rate the audio data should be converted to.
	*/
	float fDesiredSampleRate = 48000;

	/** The file type that should be used to write the audio data.
	*/
	AudioFileFormat.Type desiredFileType = AudioFileFormat.Type.WAVE;

	/** The endianess the audio data should be converted to.
		This is only used if bIsEndianessDesired is true.
	*/
	boolean bDesiredBigEndian = false;//little endian

	/** Whether conversion of endianess is desired. This flag is
		necessary because the boolean variable bDesiredBigEndian
		has no 'unspecified' value to signal that endianess
		conversion is not desired.
	*/
	boolean bIsEndianessDesired = true;
	
		AudioFileFormat inputFileFormat;
		try {
			inputFileFormat = AudioSystem.getAudioFileFormat(inputFile);
			AudioFileFormat.Type	defaultFileType = inputFileFormat.getType();
			
			AudioInputStream	stream = null;
			stream = AudioSystem.getAudioInputStream(inputFile);
			AudioFormat	format = stream.getFormat();
			
			
			if (! isPcm(format.getEncoding()))
			{
			log.warn("file not wav");
			return null;
			}

			/* Step 2: convert number of channels, if necessary.
			*/
			if (stream.getFormat().getChannels() != nDesiredChannels)
			{
				log.info("converting channels...");
				stream = convertChannels(nDesiredChannels, stream);
				log.info("stream: " + stream);
				log.info("format: " + stream.getFormat());
			}

			/* Step 3: convert sample size and endianess, if necessary.
			*/
			boolean bDoConvertSampleSize =
				(stream.getFormat().getSampleSizeInBits() != nDesiredSampleSizeInBits);
			boolean bDoConvertEndianess =
				(stream.getFormat().isBigEndian() != bDesiredBigEndian);
			if (bDoConvertSampleSize || bDoConvertEndianess)
			{
				log.info("converting sample size and endianess...");
				stream = convertSampleSizeAndEndianess(nDesiredSampleSizeInBits,
													   bDesiredBigEndian, stream);
				log.info("stream: " + stream);
				log.info("format: " + stream.getFormat());
			}

			/* Step 4: convert sample rate, if necessary.
			*/
			if (! equals(stream.getFormat().getSampleRate(), fDesiredSampleRate))
			{
				log.info("converting sample rate...");
				stream = convertSampleRate(fDesiredSampleRate, stream);
				log.info("stream: " + stream);
				log.info("format: " + stream.getFormat());
			}

			/* Step 5: convert to non-PCM encoding, if necessary.
			*/
			if (! stream.getFormat().getEncoding().equals(desiredEncoding))
			{
				log.info("converting to " + desiredEncoding + "...");
				stream = convertEncoding(desiredEncoding, stream);
				log.info("stream: " + stream);
				log.info("format: " + stream.getFormat());
			}
			
			return stream;
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}
	
	

	public static AudioInputStream convertEncoding(
		AudioFormat.Encoding targetEncoding,
		AudioInputStream sourceStream)
	{
		return AudioSystem.getAudioInputStream(targetEncoding,
											   sourceStream);
	}


	public static AudioInputStream convertChannels(
		int nChannels,
		AudioInputStream sourceStream)
	{
		AudioFormat sourceFormat = sourceStream.getFormat();
		AudioFormat targetFormat = new AudioFormat(
			sourceFormat.getEncoding(),
			sourceFormat.getSampleRate(),
			sourceFormat.getSampleSizeInBits(),
			nChannels,
			calculateFrameSize(nChannels,
							   sourceFormat.getSampleSizeInBits()),
			sourceFormat.getFrameRate(),
			sourceFormat.isBigEndian());
		return AudioSystem.getAudioInputStream(targetFormat,
											   sourceStream);
	}


	public static AudioInputStream convertSampleSizeAndEndianess(
		int nSampleSizeInBits,
		boolean bBigEndian,
		AudioInputStream sourceStream)
	{
		AudioFormat sourceFormat = sourceStream.getFormat();
		AudioFormat targetFormat = new AudioFormat(
			sourceFormat.getEncoding(),
			sourceFormat.getSampleRate(),
			nSampleSizeInBits,
			sourceFormat.getChannels(),
			calculateFrameSize(sourceFormat.getChannels(),
							   nSampleSizeInBits),
			sourceFormat.getFrameRate(),
			bBigEndian);
		return AudioSystem.getAudioInputStream(targetFormat,
											   sourceStream);
	}


	public static AudioInputStream convertSampleRate(
		float fSampleRate,
		AudioInputStream sourceStream)
	{
		AudioFormat sourceFormat = sourceStream.getFormat();
		AudioFormat targetFormat = new AudioFormat(
			sourceFormat.getEncoding(),
			fSampleRate,
			sourceFormat.getSampleSizeInBits(),
			sourceFormat.getChannels(),
			sourceFormat.getFrameSize(),
			fSampleRate,
			sourceFormat.isBigEndian());
		return AudioSystem.getAudioInputStream(targetFormat,
											   sourceStream);
	}
	
	private static int calculateFrameSize(int nChannels, int nSampleSizeInBits)
	{
		return ((nSampleSizeInBits + 7) / 8) * nChannels;
	}


	/** Compares two float values for equality.
	 */
	private static boolean equals(float f1, float f2)
	{
		return (Math.abs(f1 - f2) < DELTA);
	}
	
	public static boolean isPcm(AudioFormat.Encoding encoding)
	{
		return encoding.equals(AudioFormat.Encoding.PCM_SIGNED)
			|| encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED);
	}
	
	private static final float DELTA = 1E-9F;
}
